# TODO: ААААААААААААААААААА Хвърляй грешки както трябва, по дяволите!!!
# В момента, ако нещо не сработи, просто ти казва "Еми, шанс"...
class Problem < ActiveRecord::Base
  include Configurator 
  include FileMover
  require 'fileutils'
    
  validates_presence_of :name
  validates_presence_of :letter
  validates_presence_of :time_limit
  validates_numericality_of :time_limit,:contest_id
  validates_presence_of :contest_id
  validates_format_of :letter,:with => /^[A-Z]$/ 
  validates_format_of :name,:with => /^.*$/
  validates_presence_of :contest,:about
  validates_inclusion_of :time_limit, :in => 1..60, :message => "not in the allowed interval(1..60)"
  validate :validate_description
  validate :validate_answers
  validate :validate_tests
  validate_on_update :can_update_directory
  validate_on_create :correct_directory
  validate_on_create :has_description,:has_answers,:has_tests
  
  after_create :make_directory
  before_destroy :remove_dir
  belongs_to :contest
  has_many :runs
  self.primary_key = "problem_id"


  def description= file
     self.description_filename = "description_#{self.contest.set_code}_#{self.letter}.#{type(file)}" if file
     @description_file = file
  end

  def description
    path = directory
    if self.description_filename != ""
        return File.join(path,self.description_filename)
    # Това е за Backward Compatibility, в скоро време трябва да го махна
    elsif FileTest.exists? File.join(path,"description.doc")
      return File.join(path,"description.doc")
    elsif FileTest.exists? File.join(path,"description.pdf")
      return File.join(path,"description.pdf")
    else
      return false
    end
  end

  def tests= file
    @tests_file = file
  end

  def tests
    config = load_config
    path = directory
    if FileTest.exists? File.join(path,config['test_file'])
      return File.join(path,config['test_file'])
    else 
      false
    end
  end
   
  def answers= file
    @answers_file = file
  end

  def answers
    config = load_config
    path = directory
    if FileTest.exists? File.join(path,config['answer_file'])
      return File.join(path,config['answer_file'])
    else 
      false
    end
  end

  alias old_save save
  def save
    return false unless old_save
    config = load_config

    upload(@description_file,"description_#{self.contest.set_code}_#{self.letter}.#{type(@description_file)}",directory) if @description_file
    upload(@tests_file,config['test_file'],directory) if @tests_file
    upload(@answers_file,config['answer_file'],directory) if @answers_file

    add_info
    @description_file = nil
    @tests_file = nil
    @answers_file = nil
    true
  end

  def directory    
    if !self.name
     raise ArgumentError.new("directory method called for a problem without a name")
    end
    config = load_config
    path = File.join(config['base_dir'],"sets")
    path = File.join(path,self.contest.set_code)
    path = File.join(path,code_name)
    path
  end

  def synchronize
    make_directory unless FileTest.exists? directory
    add_info
  end

  def code_name 
    self.name.strip.downcase.gsub(/\s+/,'-')
  end
  private 

  def change_directory_if_necessary
    p = Problem.find_by_problem_id(self.problem_id)
    return if p.name == self.name 
    to = directory
    move_dir(p.directory,to)
  end

  def make_directory 
    FileUtils.mkdir directory unless FileTest.exists? directory
  end

  def type file
    name = file.original_filename
    name.match(/\.(\w*)$/)[1]
  end

  def validate_answers
    if @answers_file.class == String
      self.errors.add("answers","are not provided!")
      false
    end
  end

  def validate_tests
    if @tests_file.class == String
      self.errors.add("tests","are not provided!")
      false
    end
  end

  def validate_description
    return true unless @description_file
    if @description_file.class == String
      self.errors.add("description","is not provided!")
      false
    else
      config = load_config
      name = @description_file.original_filename
      filetype = name.match(/\.(\w*)$/)[1]
      unless config['allowed_description_types'].include?(filetype)
        self.errors.add("description","is of the wrong type(allowed:#{config['allowed_description_types']}, was: #{filetype})")
        false
      end
    end
  end

  def upload(file,name,destination)
    path = File.join(destination, name)
    File.open(path, "wb") { |f| f.write(file.read) }
  end

  def add_info
    path = directory
    path = File.join(path,"problem-info.conf")
    File.open(path,"w") do |f|
      f.puts "name=#{self.name}"
      f.puts "time_limit=#{self.time_limit}"
      f.puts "about=#{self.about}"
    end
  end

  def correct_directory
    names = Problem.find(:all,:conditions => {:contest_id => self.contest_id}).collect{|p| p.name}
    names.map! {|name| name.strip.downcase.gsub(/\s+/,'-')}
    if FileTest.exists? directory and names.include? code_name
      self.errors.add("name","the directory already exists #{directory}")
      false
    end
  end

  def can_update_directory
    return false if !self.contest || !self.name || !self.contest.set_code 
    p = Problem.find(:first,:conditions => {:contest_id => self.contest,:name => self.name})
    if(p && p.problem_id != self.problem_id)
      correct_directory
    else
      change_directory_if_necessary
    end
  end

  def has_description
    if !@description_file
      self.errors.add("description","no description uploaded")
      false
    end
  end

  def has_answers
    if !@answers_file
      self.errors.add("answers","no answers uploaded")
      false
    end
  end

  def has_tests
    if !@tests_file
      self.errors.add("tests","no tests uploaded")
      false
    end
  end

  protected

  def remove_dir 
    d = Dir.new directory
    d.each do |t|
      FileUtils.rm(File.join(d.path,t)) unless t=~ /^\..*/
    end
    FileUtils.rmdir d.path
  end
end
